package top.xia17.plugins.fast.common.database.mybatis.mapping;

import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.Configuration;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * mybatis字段自动映射工具类
 * @author xia17
 * @since 2021/7/16 13:42
 */
public class IbatisColumnUtils {

    public static final char UNDERLINE = '_';

    public static void handle(Configuration configuration , IbatisMappingConfig mappingConfig)
            throws NoSuchFieldException, IllegalAccessException {
        //为防止重复处理,这里进行一次缓存.
        Map<Class<?>, List<ResultMapping>> typeResultMap  = new HashMap<>(128);

        //设置 configuration 中的 resultMaps
        Field resultMapsField = configuration.getClass().getDeclaredField("resultMaps");
        resultMapsField.setAccessible(true);
        Map<String, ResultMap> resultMapMap = (Map<String, ResultMap>) resultMapsField.get(configuration);
        Map<String, ResultMap> resultMapProxy = new HashMap<>(128);
        for (Map.Entry<String, ResultMap> entry : resultMapMap.entrySet()) {
            ResultMap map = processColumn(configuration, entry.getValue(), typeResultMap,mappingConfig);
            resultMapProxy.put(entry.getKey(), map);
        }
        resultMapsField.set(configuration, resultMapProxy);
        // 设置 configuration 中的 mappedStatements.
        Field mappedStatementsField = configuration.getClass().getDeclaredField("mappedStatements");
        mappedStatementsField.setAccessible(true);
        Map<String, MappedStatement> mappedStatement = (Map<String, MappedStatement>) mappedStatementsField.get(configuration);
        // resultMaps 在 MappedStatement 中,所在需要再一次进行反射.
        Field resultMapsMsField = MappedStatement.class.getDeclaredField("resultMaps");
        resultMapsMsField.setAccessible(true);
        for (Map.Entry<String, MappedStatement> stringMappedStatementEntry : mappedStatement
                .entrySet()) {
            List<ResultMap> resultMaps = new ArrayList<>();
            for (ResultMap resultMap : stringMappedStatementEntry.getValue().getResultMaps()) {
                resultMaps.add(processColumn(configuration, resultMap, typeResultMap,mappingConfig));
            }
            resultMapsMsField.set(stringMappedStatementEntry.getValue(), resultMaps);
        }
    }

    private static ResultMap processColumn(Configuration configuration, ResultMap resultMap,
                                           Map<Class<?>, List<ResultMapping>> typeResultMap ,
                                           IbatisMappingConfig mappingConfig) {
        Class<?> type = resultMap.getType();
        IbatisQueryDto ibatisQueryDto = type.getAnnotation(IbatisQueryDto.class);
        if (null == ibatisQueryDto && !mappingConfig.packageContainDto(type)) {
            return resultMap;
        }
        if (!typeResultMap.containsKey(type)) {
            List<ResultMapping> resultMappingList = new ArrayList<>();
            for (Field field : type.getDeclaredFields()) {
                IbatisQueryColumn column = field.getAnnotation(IbatisQueryColumn.class);
                // 获取column名
                String columnName = getFinalColumnName(field.getName(), ibatisQueryDto, column,mappingConfig);
                resultMappingList.add(new ResultMapping.Builder(configuration, field.getName(),
                        columnName, field.getType()).build());
            }
            typeResultMap.put(type, resultMappingList);
        }
        return new ResultMap.Builder(configuration, resultMap.getId(), resultMap.getType(), typeResultMap.get(type)).build();
    }


    private static String getFinalColumnName(String fieldName , IbatisQueryDto ibatisQueryDto , IbatisQueryColumn ibatisQueryColumn , IbatisMappingConfig mappingConfig){
        // 是否写了配置了value
        if (ibatisQueryColumn != null && StrUtil.isNotBlank(ibatisQueryColumn.value())){
            return ibatisQueryColumn.value();
        }
        // 驼峰转下划线
        String columnName = camelToUnderline(fieldName, ibatisQueryDto.toUpperCase());
        // 添加前缀 注解自定义
        if (StrUtil.isNotBlank(ibatisQueryDto.columnPrefix())){
            return ibatisQueryDto.columnPrefix() + columnName;
        }
        // 全局前缀配置
        if (StrUtil.isNotBlank(mappingConfig.getColumnPrefix())){
            return mappingConfig.getColumnPrefix() + columnName;
        }
        // 是否加上转义符
        String[] escapes = ibatisQueryDto.escapes();
        if (escapes.length != 2){
            return columnName;
        }
        return escapes[0] + columnName + escapes[1];
    }

    /**
     * 驼峰转下划线
     * @param param /
     * @param toUpperCase /
     * @return /
     */
    private static String camelToUnderline(String param, boolean toUpperCase) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
            }
            if (toUpperCase) {
                //统一都转大写
                sb.append(Character.toUpperCase(c));
            } else {
                //统一都转小写
                sb.append(Character.toLowerCase(c));
            }
        }
        return sb.toString();
    }

}
